#  -*- coding: UTF-8 -*-
# import operator
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.lines as mlines
from matplotlib.font_manager import FontProperties
import pandas as pd
import operator
from os import listdir
from math import log


def createDataSet():
    # return: array,list
    # 四组二维特征
    group = np.array([[1, 101], [5, 89], [108, 5], [115, 8]])
    # 四组特征的标签
    labels = [u"爱情片", u"爱情片", u"动作片", u"动作片"]
    return group, labels


def showDataSet(x, y, title='figure'):
    plt.title(title)
    plt.xlabel("x axis")
    plt.ylabel("y axis")
    plt.plot(x, y)
    plt.show()


def classify0(inX, dataSet, labels, k):
    # Param:array-1*2,category,list,int
    # return:category-name,category-num
    # 注意使用矩阵计算代替循环
    ED = EDistance(inX, dataSet)  # 点x与点集Y的欧式距离
    SED = ED.argsort()
    labelCount = [0] * labels.categories.size
    for i in range(k):
        labelNum = labels.codes[SED[i]]
        labelCount[labelNum] += 1
    categoryNum = np.argmax(labelCount)
    categoryName = labels.categories[categoryNum]
    return categoryName, categoryNum


def EDistance(x, Y):
    # Para:array-n*2
    diff = x - Y  # 借助numpy的广播
    return ((diff**2).sum(1))**0.5


# 功能：读取数据文本返回数据和标签
def file2matrix(fileName):
    # param:str
    # return:matrix,category
    dataSet_fl = open(fileName)
    dataSet = dataSet_fl.readlines()
    dataSet_fl.close()
    returnMatrix = np.zeros([len(dataSet), 3])
    labels = []
    i = 0
    for line in dataSet:
        line = line.strip()
        dataLine = line.split("\t")
        returnMatrix[i, ] = dataLine[0:3]
        labels.append(dataLine[-1])
        i += 1
    return returnMatrix, pd.Categorical(labels)


# 功能：显示读取的文本离散数据
def showData(DataSet, labels):
    # param:array,category
    # return:none
    figureFont = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)
    fig, axs = plt.subplots(3, 1)
    plt.subplots_adjust(hspace=0.82)
    labelColors = []
    for i in labels.codes:
        if i == 0:
            labelColors.append('black')
        elif i == 1:
            labelColors.append('orange')
        else:
            labelColors.append('red')
    axs[0].scatter(x=DataSet[:, 0],
                   y=DataSet[:, 1],
                   color=labelColors,
                   s=15,
                   alpha=0.5)
    axs[0].set_title(u'飞行常客里程数和Game', FontProperties=figureFont)
    axs[0].set_xlabel(u'飞行常客里程数', FontProperties=figureFont)
    axs[0].set_ylabel(u'Game', FontProperties=figureFont)
    axs[1].scatter(x=DataSet[:, 0],
                   y=DataSet[:, 2],
                   color=labelColors,
                   s=15,
                   alpha=0.5)
    axs[1].set_title(u'飞行常客里程和冰淇淋消费', FontProperties=figureFont)
    axs[1].set_xlabel(u'飞行常客里程费', FontProperties=figureFont)
    axs[1].set_ylabel(u'冰淇淋消费', FontProperties=figureFont)

    axs[2].scatter(x=DataSet[:, 1],
                   y=DataSet[:, 2],
                   color=labelColors,
                   s=15,
                   alpha=0.5)
    axs[2].set_title(u'Game和冰淇淋消费', FontProperties=figureFont)
    axs[2].set_xlabel(u'Game', FontProperties=figureFont)
    axs[2].set_ylabel(u'冰淇淋消费', FontProperties=figureFont)
    dl = mlines.Line2D([], [],
                       color='black',
                       marker='.',
                       markersize=6,
                       label=labels.categories[0])
    sl = mlines.Line2D([], [],
                       color='orange',
                       marker='.',
                       markersize=6,
                       label=labels.categories[1])
    ll = mlines.Line2D([], [],
                       color='red',
                       marker='.',
                       markersize=6,
                       label=labels.categories[2])
    axs[0].legend(handles=[dl, sl, ll])
    axs[1].legend(handles=[dl, sl, ll])
    axs[2].legend(handles=[dl, sl, ll])
    plt.show()


# 功能：归一化数据
def autoNorm(dataSet):
    # param:array
    # return:array*3

    returnDataSet = np.zeros(dataSet.shape)
    #  np.zeros([dataSet.shape[0], dataSet.shape[1]]) 这样写好蠢
    minVector = dataSet.min(0)
    maxVector = dataSet.max(0)
    ranges = maxVector - minVector
    if ranges.dtype != float:
        ranges = ranges.astype(float)
    returnDataSet = (dataSet - minVector) / ranges
    return returnDataSet, ranges, minVector


# 功能：
def datingClassTest(dataSet, labels, k):
    # param:array,category
    # return:
    # 10%做测试集,剩下的为训练集;
    # 测试集：0-testNum-1;训练集testNum-len-1
    testNum = int(dataSet.shape[0] * 0.1)
    errCount = 0
    errCount2 = 0
    for i in range(testNum):
        result = classify0(dataSet[i, :], dataSet[testNum:, :],
                           labels[testNum:], k)
        result2 = classify2(dataSet[i, :], dataSet[testNum:, :],
                            labels[testNum:], k)
        if result != labels.codes[i]:
            errCount += 1
            print u"我错test:%s;real:%s" % (labels.categories[result], labels[i])
            print "test:%s;real:%s" % (result2, labels[i])
        if result2 != labels[i]:
            errCount2 += 1
            print u"他错test:%s;real:%s" % (labels.categories[result], labels[i])
            print "test:%s;real:%s" % (result2, labels[i])
    errRatio = errCount / float(testNum) * 100.0
    errRatio2 = errCount2 / float(testNum) * 100.0

    print u"错误率：%f%%" % (errRatio)
    print u"错误率2：%f%%" % (errRatio2)
    return errRatio


# 功能：将32*32图片转为1*1024的numpy向量
def img2vector(fileName):
    # param:path
    # return:array
    fh = open(fileName)
    returnData = np.zeros([1, 1024], int)
    for i in range(32):
        lineTest = fh.readline()
        for j in range(32):
            returnData[0, i * 32 + j] = int(lineTest[j])
    return returnData


# 功能：将目录下的文件读取到矩阵中，利用img2vector函数读文件
def dir2matrix(path):
    # param:path
    # return:array_int-data,category
    fileNames = listdir(path)
    fileNum = len(fileNames)
    returnData = np.zeros([fileNum, 1024], int)
    labels = [-1] * fileNum
    for i in range(fileNum):
        returnData[i, ] = img2vector(path + '\\' + fileNames[i])
        labels[i] = fileNames[i].split('_')[0]  # 注意是'0' 不是int：0

    return returnData, pd.Categorical(labels)


# debug
def classify2(inX, dataSet, labels, k):
    # numpy函数shape[0]返回dataSet的行数
    dataSetSize = dataSet.shape[0]
    #  在列向量方向上重复inX共1次(横向),行向量方向上重复inX共dataSetSize次(纵向)
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    # 二维特征相减后平方
    sqDiffMat = diffMat**2
    # sum()所有元素相加,sum(0)列相加,sum(1)行相加
    sqDistances = sqDiffMat.sum(axis=1)
    # 开方,计算出距离
    distances = sqDistances**0.5
    # 返回distances中元素从小到大排序后的索引值
    sortedDistIndices = distances.argsort()
    # 定一个记录类别次数的字典
    classCount = {}
    for i in range(k):
        # 取出前k个元素的类别
        voteIlabel = labels[sortedDistIndices[i]]
        # dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
        # 计算类别次数
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    # python3中用items()替换python2中的iteritems()
    # key=operator.itemgetter(1)根据字典的值进行排序
    # key=operator.itemgetter(0)根据字典的键进行排序
    # reverse降序排序字典
    sortedClassCount = sorted(classCount.items(),
                              key=operator.itemgetter(1),
                              reverse=True)
    # 返回次数最多的类别,即所要分类的类别
    return sortedClassCount[0][0]


# 功能：
def handWritingClassTest():
    # param:
    # return:
    # 1-收集数据-ok
    returnData = []
    # 2-准本数据：
    trainingPath = r'D:\python\Ch02\trainingDigits'
    testingPath = r'D:\python\Ch02\testDigits'
    trainingData, trainingLabels = dir2matrix(trainingPath)
    testingData, testingLabels = dir2matrix(testingPath)

    # -------------------2-ok
    # 3-分析+测试数据：
    k = 4
    errCount = 0
    # testingData.size是总数，通常我们只要行数的
    testingNum = testingData.shape[0]
    testingResult = [-1] * testingNum
    for i in range(testingNum):
        testingResult[i], categoryCode = classify0(testingData[i, ],
                                                   trainingData,
                                                   trainingLabels, k)
        if testingResult[i] != testingLabels[i]:
            errCount += 1
            print('analyse:%c;real:%c' % (testingResult[i], testingLabels[i]))
    print "errRatio:", errCount / float(testingNum) * 100.0, "%"
    return returnData


"""
# debug：calcShannonEnt,
dataSet=param1
# content:

# return:
print 'return\n'
# -------------------
"""